package com.qianfeng.smartdevice.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.qianfeng.smartdevice.cache.CategoryCache;
import com.qianfeng.smartdevice.cache.SceneCache;
import com.qianfeng.smartdevice.enums.CheckType;
import com.qianfeng.smartdevice.event.ColorCommandEvent;
import com.qianfeng.smartdevice.event.PowerCommandEvent;
import com.qianfeng.smartdevice.execption.AddDataErrorException;
import com.qianfeng.smartdevice.execption.MyErrorCode;
import com.qianfeng.smartdevice.execption.QueryDataErrorException;
import com.qianfeng.smartdevice.mapper.SysDeviceMapper;
import com.qianfeng.smartdevice.pojo.SysCategory;
import com.qianfeng.smartdevice.pojo.SysDevice;
import com.qianfeng.smartdevice.pojo.SysScene;
import com.qianfeng.smartdevice.pojo.SysUserInfo;
import com.qianfeng.smartdevice.service.SysDeviceService;
import com.qianfeng.smartdevice.utils.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * Created by wenka on 2022/3/25 20:15
 *
 * @author Jackiechan
 * @version 1.0
 * @since 1.0
 */
@Service
@Transactional
public class SysDeviceServiceImpl implements SysDeviceService {
    private SysDeviceMapper sysDeviceMapper;
    private SceneCache sceneCache;
    private CategoryCache categoryCache;
    private ObjectMapper objectMapper;
    private ApplicationContext context;

    @Autowired
    public void setContext(ApplicationContext context) {
        this.context = context;
    }

    @Autowired
    public void setObjectMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Autowired
    public void setCategoryCache(CategoryCache categoryCache) {
        this.categoryCache = categoryCache;
    }

    @Autowired
    public void setSceneCache(SceneCache sceneCache) {
        this.sceneCache = sceneCache;
    }

    @Autowired
    public void setSysDeviceMapper(SysDeviceMapper sysDeviceMapper) {
        this.sysDeviceMapper = sysDeviceMapper;
    }

    @Override
    public void addSysDevice(SysDevice device) {
        //传递数据不能为空
        Assert.isTrue(!device.isEmpty(CheckType.ADD), () -> {
            throw new AddDataErrorException("传递的数据不能为空", MyErrorCode.DATA_NULL);
        });
        //获取登录用户
        SysUserInfo loginUser = SecurityUtils.getLoginUser();
        //我们要求必须登录
        Assert.notNull(loginUser, () -> {
            throw new AddDataErrorException("未登录", MyErrorCode.NOT_LOGIN);
        });
        //这个登录用户一定有值
        device.setBindUserId(loginUser.getUId());
        //检查设备是不是已经被绑定
        SysDevice deviceMapperById = sysDeviceMapper.findById(device.getDeviceId());
        Assert.isNull(deviceMapperById, () -> {
            throw new AddDataErrorException("当前设备已经被绑定", MyErrorCode.DEVICE_ALLREADY_BIND);
        });
        //检查场景是不是空的，是空的就设置为-1；不是空的就检查这个场景属于不属于当前用户,因为不能添加到别人的场景中
        if (device.getSceneId() == null) {
            device.setSceneId(-1L);
        } else if (device.getSceneId() != -1) {
            //传递了场景id，并且不是-1，需要判断是不是属于当前用户
            //我们有场景的缓存，可以从缓存中查询这个数据
            SysScene sysScene = sceneCache.get(device.getSceneId());
            //这个场景可能不存在，也可能不属于当前用户，但是不管什么，只要不符号，我们就提示场景不存在
            Assert.isNull(sysScene != null && sysScene.equals(loginUser.getUId()), () -> {
                throw new AddDataErrorException("场景不存在", MyErrorCode.SCENE_NOT_EXIST);
            });
        }
        //检查分类是不是存在
        SysCategory category = categoryCache.getWithDelete(device.getCategyId());
        Assert.notNull(category, () -> {
            throw new AddDataErrorException("分类不存在", MyErrorCode.CATEGORY_NOT_EXIST);
        });
        device.setBindTime(new Date());
        sysDeviceMapper.addSysDevice(device);


    }

    @Override
    public SysDevice findById(String deviceId) {
        //获取登录用户
        SysUserInfo loginUser = SecurityUtils.getLoginUser();
        //我们要求必须登录
        Assert.notNull(loginUser, () -> {
            throw new AddDataErrorException("未登录", MyErrorCode.NOT_LOGIN);
        });
        SysDevice sysDevice = sysDeviceMapper.findById(deviceId);
        //判断是不是当前用户绑定的
        Assert.isTrue(sysDevice == null || loginUser.getUId().equals(sysDevice.getBindUserId()), () -> {
            throw new QueryDataErrorException("设备不存在", MyErrorCode.DEVICE_NOT_EXIST);
        });

        return sysDevice;
    }

    @Override
    public int updateDeviceById(SysDevice sysDevice) {
        //TODO 校验数据
        int result = sysDeviceMapper.updateDeviceById(sysDevice);
        return result;
    }

    @Override
    public PageInfo<SysDevice> findAll(int page, int limit, SysDevice sysDevice) {
        PageHelper.startPage(page, limit);
        List<SysDevice> deviceList = sysDeviceMapper.findAll(sysDevice);
        deviceList.forEach(sysDevice1 -> {
            //当前设备关联的分类数据没有查询
            Long categoryId = sysDevice1.getCategyId();
            //我们有一个分类缓存，分类缓存中提供了一个根据id查询分类的功能
            SysCategory sysCategory = categoryCache.getWithDelete(categoryId);
            //给当前的设备设置分类
            sysDevice1.setCategory(sysCategory);
        });
        PageInfo<SysDevice> pageInfo = new PageInfo<>(deviceList);
        return pageInfo;
    }

    @Override
    public void sendControl(String deviceId, String command) {
        //发送命令给指定的设备
        //我们得先找到要发送得命令是什么
        //命令在设备得分类中，我们传递了一个标识过来
        //我们得先找到分类，分类得id在设备得数据中
        //我们现在知道一个设备得id，通过设备得id查询到分类得id，然后根据分类得id查询到命令，解析命令，然后根据传递得command找到真正得命令
        SysDevice device = sysDeviceMapper.findById(deviceId);
        Assert.notNull(device, () -> {
            throw new QueryDataErrorException("设备不存在", MyErrorCode.DEVICE_NOT_EXIST);
        });
        //分类id
        Long categyId = device.getCategyId();
        //从缓存中找到分类
        SysCategory category = categoryCache.getWithDelete(categyId);
        //获取到命令
        String txCommand = category.getTxCommand();
        Assert.hasText(txCommand, () -> {
            throw new QueryDataErrorException("操作不存在", MyErrorCode.PERMSSION_NOT_ALLOWED);
        });
        try {
            //解析命令
            Map map = objectMapper.readValue(txCommand, Map.class);
            //获得当前操作对应的命令
            Object o = map.get(command);
            if (o instanceof Map) {
                //命令的类型，1是给设备发送，2是给对应得颜色控制器发送
                int type = (int) ((Map) o).get("type");
                //对应得真实得命令
                String realCommand = (String) ((Map<?, ?>) o).get("command");
                switch (type) {
                    case 1:
                        //给设备发送map中command值出去，设备是要通过websocket进行发送命令的
                        context.publishEvent(new PowerCommandEvent(deviceId,realCommand));
                        break;
                    case 2:
                        //给颜色控制器发送命令
                        context.publishEvent(new ColorCommandEvent(deviceId,realCommand));
                        break;
                }
            }
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

    }
}
